// Copyright (C) 1984-1998 by Symantec
// Copyright (C) 2000-2009 by Digital Mars
// All Rights Reserved
// http://www.digitalmars.com
// Written by Walter Bright
/*
 * This source file is made available for personal use
 * only. The license is in /dmd/src/dmd/backendlicense.txt
 * For any other uses, please contact Digital Mars.
 */

#if !SPP

#include	<stdio.h>
#include	<string.h>
#include	<stdlib.h>
#include	<time.h>
#include	"cc.h"
#include	"oper.h"
#include	"global.h"
#include	"parser.h"
#include	"el.h"
#include	"type.h"
#include	"dt.h"

static char __file__[] = __FILE__;	/* for tassert.h		*/
#include	"tassert.h"

static dt_t *dt_freelist;

/**********************************************
 * Allocate a data definition struct.
 */
 
dt_t *dt_calloc(char dtx)
{
    dt_t *dt;
    static dt_t dtzero;
     
    if (dt_freelist)
    {
	dt = dt_freelist;
	dt_freelist = dt->DTnext;
	*dt = dtzero;
    }
    else
#if TX86
	dt = (dt_t *) mem_fcalloc(sizeof(dt_t));
#else
	dt = (dt_t *) MEM_PH_MALLOC(sizeof(dt_t));
#endif
    dt->dt = dtx;
    return dt;
}

/**********************************************
 * Free a data definition struct.
 */

void dt_free(dt_t *dt)
{   dt_t *dtn;

    for (; dt; dt = dtn)
    {
	switch (dt->dt)
	{
	    case DT_abytes:
	    case DT_nbytes:
		mem_free(dt->DTpbytes);
		break;
	}
	dtn = dt->DTnext;
	dt->DTnext = dt_freelist;
	dt_freelist = dt;
    }
}

/*********************************
 * Free free list.
 */

void dt_term()
{
#if TERMCODE
    dt_t *dtn;

    while (dt_freelist)
    {	dtn = dt_freelist->DTnext;
#if TX86
	mem_ffree(dt_freelist);
#else
	MEM_PH_FREE(dt_freelist);
#endif
	dt_freelist = dtn;
    }
#endif
}


/**********************
 * Construct a DT_azeros record, and return it.
 * Increment dsout.
 */

dt_t **dtnzeros(dt_t **pdtend,targ_size_t size)
{   dt_t *dt;

    //printf("dtnzeros(x%x)\n",size);
    assert((long) size >= 0);
    while (*pdtend)
	pdtend = &((*pdtend)->DTnext);
    if (size)
    {	dt = dt_calloc(DT_azeros);
	dt->DTazeros = size;
	*pdtend = dt;
	pdtend = &dt->DTnext;
#if SCPP
	dsout += size;
#endif
    }
    return pdtend;
}

/**********************
 * Construct a DTsymsize record.
 */

void dtsymsize(symbol *s)
{
    symbol_debug(s);
    s->Sdt = dt_calloc(DT_symsize);
}

/**********************
 * Construct a DTnbytes record, and return it.
 */

dt_t ** dtnbytes(dt_t **pdtend,targ_size_t size,const char *ptr)
{   dt_t *dt;

    while (*pdtend)
	pdtend = &((*pdtend)->DTnext);
    if (size)
    {	if (size == 1)
	{   dt = dt_calloc(DT_1byte);
	    dt->DTonebyte = *ptr;
	}
	else if (size <= 7)
	{   dt = dt_calloc(DT_ibytes);
	    dt->DTn = size;
	    memcpy(dt->DTdata,ptr,size);
	}
	else
	{
	    dt = dt_calloc(DT_nbytes);
	    dt->DTnbytes = size;
	    dt->DTpbytes = (char *) MEM_PH_MALLOC(size);
	    memcpy(dt->DTpbytes,ptr,size);
	}
	*pdtend = dt;
	pdtend = &dt->DTnext;
    }
    return pdtend;
}

/**********************
 * Construct a DTabytes record, and return it.
 */

dt_t **dtabytes(dt_t **pdtend,tym_t ty, targ_size_t offset, targ_size_t size, const char *ptr)
{   dt_t *dt;

    while (*pdtend)
	pdtend = &((*pdtend)->DTnext);

    dt = dt_calloc(DT_abytes);
    dt->DTnbytes = size;
    dt->DTpbytes = (char *) MEM_PH_MALLOC(size);
    dt->Dty = ty;
    dt->DTabytes = offset;
    memcpy(dt->DTpbytes,ptr,size);

    *pdtend = dt;
    pdtend = &dt->DTnext;
    return pdtend;
}

/**********************
 * Construct a DTibytes record, and return it.
 */

dt_t ** dtdword(dt_t **pdtend,long value)
{   dt_t *dt;

    while (*pdtend)
	pdtend = &((*pdtend)->DTnext);
    dt = dt_calloc(DT_ibytes);
    dt->DTn = 4;
    *(long *)dt->DTdata = value;
    *pdtend = dt;
    pdtend = &dt->DTnext;
    return pdtend;
}

/**********************
 * Concatenate two dt_t's.
 */

dt_t ** dtcat(dt_t **pdtend,dt_t *dt)
{
    while (*pdtend)
	pdtend = &((*pdtend)->DTnext);
    *pdtend = dt;
    pdtend = &dt->DTnext;
    return pdtend;
}

/**********************
 * Construct a DTcoff record, and return it.
 */

dt_t ** dtcoff(dt_t **pdtend,targ_size_t offset)
{   dt_t *dt;

    while (*pdtend)
	pdtend = &((*pdtend)->DTnext);
    dt = dt_calloc(DT_coff);
    dt->Dty = TYcptr;
    dt->DToffset = offset;
    *pdtend = dt;
    pdtend = &dt->DTnext;
    return pdtend;
}

/**********************
 * Construct a DTxoff record, and return it.
 */

dt_t ** dtxoff(dt_t **pdtend,symbol *s,targ_size_t offset,tym_t ty)
{   dt_t *dt;

    symbol_debug(s);
    while (*pdtend)
	pdtend = &((*pdtend)->DTnext);
    dt = dt_calloc(DT_xoff);
    dt->DTsym = s;
    dt->DToffset = offset;
    dt->Dty = ty;
    *pdtend = dt;
    pdtend = &dt->DTnext;
    return pdtend;
}

/**************************
 * 'Optimize' a list of dt_t's.
 * (Try to collapse it into one DT_azeros object.)
 */

void dt_optimize(dt_t *dt)
{   dt_t *dtn;

    if (dt)
    {	for (; 1; dt = dtn)
	{
	    dtn = dt->DTnext;
	    if (!dtn)
		break;
	    switch (dt->dt)
	    {
		case DT_azeros:
		    if (dtn->dt == DT_1byte && dtn->DTonebyte == 0)
		    {
			dt->DTazeros += 1;
			goto L1;
		    }
		    else if (dtn->dt == DT_azeros)
		    {
			dt->DTazeros += dtn->DTazeros;
			goto L1;
		    }
		    break;

		case DT_1byte:
		    if (dt->DTonebyte == 0)
		    {
			if (dtn->dt == DT_1byte && dtn->DTonebyte == 0)
			{
			    dt->DTazeros = 2;
			    goto L1;
			}
			else if (dtn->dt == DT_azeros)
			{
			    dt->DTazeros = 1 + dtn->DTazeros;
			 L1:
			    dt->dt = DT_azeros;
			    dt->DTnext = dtn->DTnext;
			    dtn->DTnext = NULL;
			    dt_free(dtn);
			    dtn = dt;
			}
		    }
		    break;
	    }
	}
    }
}

/**************************
 * Make a common block for s.
 */

void init_common(symbol *s)
{
    //printf("init_common('%s')\n", s->Sident);
    dtnzeros(&s->Sdt,type_size(s->Stype));
    if (s->Sdt)
	s->Sdt->dt = DT_common;
}

/**********************************
 * Compute size of a dt
 */

unsigned dt_size(dt_t *dtstart)
{   dt_t *dt;
    unsigned datasize;

    datasize = 0;
    for (dt = dtstart; dt; dt = dt->DTnext)
    {
	switch (dt->dt)
	{   case DT_abytes:
		datasize += size(dt->Dty);
		break;
	    case DT_ibytes:
		datasize += dt->DTn;
		break;
	    case DT_nbytes:
		datasize += dt->DTnbytes;
		break;
	    case DT_symsize:
	    case DT_azeros:
		datasize += dt->DTazeros;
		break;
	    case DT_common:
		break;
	    case DT_xoff:
	    case DT_coff:
		datasize += size(dt->Dty);
		break;
	    case DT_1byte:
		datasize++;
		break;
	    default:
#ifdef DEBUG
		dbg_printf("dt = x%p, dt = %d\n",dt,dt->dt);
#endif
		assert(0);
	}
    }
    return datasize;
}

#endif /* !SPP */
